#!/usr/bin/python
# -*- coding: utf-8 -*-

import boto
import boto.cloudformation
import boto.vpc
import boto.ec2.autoscale
import boto.ec2.elb

import logging
import argparse
import deploy_db
import deploy_net

logging.basicConfig(level=logging.WARN,
                    format="[%(asctime)s]%(name)s:%(levelname)s:%(message)s",
                    filename='deploy.log',
                    filemode='w'
                    )
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)


class HAuthVPC:
    def _tag(self, obj, name):
        obj.add_tag('Name', name)
        obj.add_tag('Scope', SCOPE)
        obj.add_tag('Domain', DOMAIN)

    def _create_subnet(self, conn, name, vpc_id, cidr, az, is_public=False, no_creation=False):
        _rst = conn.get_all_subnets(filters=[('vpcId', vpc_id), ('cidrBlock', cidr),
                                             ('availabilityZone', az)])
        if len(_rst) == 0:
            if not no_creation:
                _net = conn.create_subnet(vpc_id, cidr, az)  # boto.vpc.customergateway.Subnet
                self._tag(_net, name)
                _associate_id = conn.associate_route_table(self.route_table[1], _net.id)
                if is_public:
                    self.pub_subnets.append((name, _net.id, cidr, az, _associate_id))
                else:
                    self.private_subnets.append((name, _net.id, cidr, az, _associate_id))
                logging.info("New subnet(" + name + ') created:' + _net.id)
        else:
            # check subnet if in route table
            try:
                _results = conn.get_all_route_tables(route_table_ids=[self.route_table['id']],
                                                     filters=[('vpc_id', vpc_id), ('tag:Name', ROUTE_TABLE_NAME)])
            except boto.exception.EC2ResponseError as e:
                if e.error_code == 'InvalidRouteTableID.NotFound':
                    pass
                else:
                    raise e
            else:
                _assc_id = None
                if len(_results) == 1:
                    for assoc in _results[0].associations:
                        if assoc.subnet_id == _rst[0].id:
                            _assc_id = assoc.id
                            break
                if not no_creation and _assc_id is None:
                    _assc_id = conn.associate_route_table(self.route_table['id'], _rst[0].id)
                    logging.warning(
                        "Existed subnet(" + _rst[0].id + ") is associated to route table(" + self.route_table[
                            'id'] + ") separately")
                if is_public:
                    self.pub_subnets.append((name, _rst[0].id, cidr, az, _assc_id))
                else:
                    self.private_subnets.append((name, _rst[0].id, cidr, az, _assc_id))
                logging.warning("Subnet(" + name + ") already exist:" + _rst[0].id)

    def __init__(self, vpc_id, image_id, elb_name=None, no_creation=False, dryrun=False):
        self.ok = False
        self.id = vpc_id
        self.pub_subnets = []  # (name,id,cidr,az,assoc id)
        self.private_subnets = []
        self.acl = {'name': None, 'id': None}
        self.route_table = {'name': None, 'id': None, 'is_public': None}
        self.sg = {'elb': None, 'server': None, 'db': None}
        self.ag = {'name': None, 'lc_name': None}
        self.acl = {'name': None, 'id': None}
        self.elb = {'name': None, 'dns': None}
        if elb_name is None:
            elb_name = HAUTH_ELB_NAME
        vpc_conn = boto.vpc.connect_to_region(REGION)  # boto.vpc.VPCConnection
        try:
            # import pdb
            # pdb.set_trace()


            # ELB security group
            ec2_conn = boto.ec2.connect_to_region(REGION)
            if ec2_conn is None:
                raise Exeception(N('Fail to connect to region({0}) by ec2', REGION))
            sgs = ec2_conn.get_all_security_groups(filters={'group_name': HAUTH_ELB_SG_NAME})
            sg_elb = None
            if len(sgs) == 0:
                if not no_creation:
                    sg_elb = ec2_conn.create_security_group(HAUTH_ELB_SG_NAME, HAUTH_ELB_SG_DESC, vpc_id=vpc_id)
                    self.sg['elb'] = sg_elb.id
            else:
                sg_elb = sgs[0]
                self.sg['elb'] = sg_elb.id

            if not no_creation:
                self._tag(sg_elb, HAUTH_ELB_SG_NAME)

            if sg_elb:
                _rule_exist = False
                for rule in sg_elb.rules:
                    if (
                            rule.ip_protocol, int(rule.from_port), int(rule.to_port),
                            [src.cidr_ip for src in rule.grants]) == \
                            ('tcp', HAUTH_PORT, HAUTH_PORT, [CIDR_ANY]):
                        _rule_exist = True
                        break
                if not no_creation and not _rule_exist:
                    sg_elb.authorize(ip_protocol='tcp', from_port=HAUTH_PORT, to_port=HAUTH_PORT, cidr_ip=CIDR_ANY)

            # ELB
            elb_conn = boto.ec2.elb.connect_to_region(REGION)
            if elb_conn is None:
                raise Exeception(N('Fail to connect to region({0}) by elb', REGION))
            elb = None
            try:
                elbs = elb_conn.get_all_load_balancers(load_balancer_names=[elb_name])
            except boto.exception.BotoServerError as e:
                if e.error_code == 'LoadBalancerNotFound':
                    if not no_creation:
                        elb = elb_conn.create_load_balancer(HAUTH_ELB_NAME, zones=[AZ1, AZ2],
                                                            listeners=None, subnets=None,
                                                            security_groups=[sg_elb], scheme='internet-facing',
                                                            complex_listeners=[
                                                                (HAUTH_PORT, HAUTH_PORT, 'HTTPS', 'HTTP', HAUTH_CERT)])
                        self.elb = {'name': HAUTH_ELB_NAME, 'dns': elb.dns_name}
                else:
                    raise e
            else:
                elb = elbs[0]
                self.elb = {'name': HAUTH_ELB_NAME, 'dns': elb.dns_name}
            if not no_creation:
                elb_conn.apply_security_groups_to_lb(HAUTH_ELB_NAME, security_groups=[sg_elb.id])
                hc = boto.ec2.elb.healthcheck.HealthCheck(access_point=HAUTH_ELB_NAME, interval=30,
                                                          target=CHECK_TARGET, healthy_threshold=3,
                                                          timeout=5, unhealthy_threshold=5)
                elb_conn.configure_health_check(HAUTH_ELB_NAME, hc)
                elb_conn.attach_lb_to_subnets(name=HAUTH_ELB_NAME,
                                              subnets=[s[1] for s in self.pub_subnets])

            # HAuth server security group
            sgs = ec2_conn.get_all_security_groups(filters={'group_name': HAUTH_SERVER_SG_NAME})
            sg = None
            if len(sgs) == 0:
                if not no_creation:
                    sg = ec2_conn.create_security_group(HAUTH_SERVER_SG_NAME, HAUTH_SERVER_SG_DESC, vpc_id=vpc_id)
                    self.sg['server'] = sg.id
            else:
                sg = sgs[0]
                self.sg['server'] = sg.id

            if not no_creation:
                self._tag(sg, HAUTH_SERVER_SG_NAME)
            if sg:
                _rule_exist = False
                for rule in sg.rules:
                    if (
                            rule.ip_protocol, int(rule.from_port), int(rule.to_port),
                            [src.group_id for src in rule.grants]) == \
                            ('tcp', HAUTH_PORT, HAUTH_PORT, [sg_elb.id]):
                        _rule_exist = True
                        break
                if not no_creation and not _rule_exist:
                    sg.authorize(ip_protocol='tcp', from_port=HAUTH_PORT, to_port=HAUTH_PORT, src_group=sg_elb)



            # auto scaling
            as_conn = boto.ec2.autoscale.connect_to_region(REGION)
            if as_conn is None:
                raise Exeception(N('Fail to connect to region({0}) by autoscale', REGION))
            # launch config
            from boto.ec2.autoscale.launchconfig import LaunchConfiguration
            from boto.ec2.autoscale import AutoScalingGroup

            lcs = as_conn.get_all_launch_configurations(names=[HAUTH_LAUNCH_CONFIG_NAME])
            lc = None
            if len(lcs) == 0:
                if not no_creation:
                    lc = LaunchConfiguration(name=HAUTH_LAUNCH_CONFIG_NAME,
                                             image_id=image_id,
                                             key_name=KEY_NAME,
                                             security_groups=[sg.id],
                                             instance_type=HAUTH_INSTANCE_TYPE,
                                             associate_public_ip_address=True,
                                             user_data='')
                    as_conn.create_launch_configuration(lc)
                    self.lc = {'name': lc.name, 'key': lc.key_name, 'image_id': lc.image_id,
                               'instance_type': lc.instance_type, 'public_ip': lc.associate_public_ip_address}
            else:
                lc = lcs[0]
                self.lc = {'name': lc.name, 'key': lc.key_name, 'image_id': lc.image_id,
                           'instance_type': lc.instance_type, 'public_ip': lc.associate_public_ip_address}

            ags = as_conn.get_all_groups([HAUTH_AG_NAME])
            if len(ags) == 0:
                if not no_creation:
                    ag = AutoScalingGroup(group_name=HAUTH_AG_NAME, load_balancers=[elb],
                                          availability_zones=[AZ1, AZ2],
                                          vpc_zone_identifier=[i[1] for i in self.pub_subnets],
                                          launch_config=lc, min_size=HAUTH_AG_MIN, max_size=HAUTH_AG_MAX,
                                          connection=as_conn)
                    as_conn.create_auto_scaling_group(ag)
                    self.ag = {"name": ag.name, "lc_name": ag.launch_config_name, "elb": ag.load_balancers}
            else:
                ag = ags[0]
                self.ag = {"name": ag.name, "lc_name": ag.launch_config_name, "elb": ag.load_balancers}

        except Exception as ex:
            logging.error(ex)
            self.ok = False
        else:
            self.ok = True

    def __str__(self):
        return "vpc:{self.id}\n\
route table:{self.route_table}\n\
network_acl:{self.acl}\n\
public subnets:{self.pub_subnets}\n\
private subnets:{self.private_subnets}\n".format(self=self)

    def destroy_server(self):
        as_conn = boto.ec2.autoscale.connect_to_region(REGION)
        if self.ag['name']:
            as_conn.delete_auto_scaling_group(self.ag['name'], force_delete=True)
        if self.ag['lc_name']:
            as_conn.delete_launch_configuration(self.ag['lc_name'])

        elb_conn = boto.ec2.elb.connect_to_region(REGION)
        if self.elb['name']:
            elb_conn.delete_load_balancer(self.elb['name'])

        ec2_conn = boto.ec2.connect_to_region(REGION)
        if self.sg['db']:
            sgs = ec2_conn.get_all_security_groups(filters={"group_id": self.sg['db']})
            if len(sgs) == 1 and self.sg['server']:
                sgs2 = ec2_conn.get_all_security_groups(filters={"group_id": self.sg['server']})
                tt = sgs[0].revoke(ip_protocol='tcp', from_port=HAUTH_DB_PORT, to_port=HAUTH_DB_PORT,
                                   src_group=sgs2[0])
                print(tt)
        if self.sg['server']:
            ec2_conn.delete_security_group(group_id=self.sg['server'])
        if self.elb['name']:
            elb_conn.delete_load_balancer(self.elb['name'])
        if self.sg['elb']:
            ec2_conn.delete_security_group(group_id=self.sg['elb'])

        print("Rollbacking...Done")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('mode', metavar='command', choices=['setup', 'query', 'remove'],
                        default='setup', type=str, help='query, setup, remove mode')
    args = parser.parse_args()
    if args.mode == 'remove':
        db = deploy_db.query_db()
        deploy_db.remove_db(db)
        net = deploy_net.query_net()
        deploy_net.remove_net(net)
    elif args.mode == 'query':
        db = deploy_db.query_db()
        net = deploy_net.query_net()
    else:
        net = deploy_net.query_net()
        deploy_net.setup_net(net)
        db = deploy_db.query_db()
        deploy_db.setup_db(db)
        print('Updated information==============')
        deploy_db.query_db()
        deploy_net
